package com.terry.proxy.socket;

import cn.hutool.core.collection.CollectionUtil;
import com.terry.proxy.application.cache.DBCache;
import com.terry.proxy.application.entity.ProxyClientTunnel;
import com.terry.proxy.common.Msg;
import com.terry.proxy.util.SocketCreateUtil;
import com.terry.proxy.util.TcpConsumer;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * socket 代理客户端 接口
 *
 * @author terry
 * @version 1.0
 * @date 2022/12/14 14:35
 */
@Slf4j
public abstract class ISocketProxyClient {

    // key 访客id ，客户端通道
    public static Map<String, Channel> realChannel = new ConcurrentHashMap<>();

    @Value("${proxy.clientId:1}")
    public String clientId;

    // 服务端channel
    public static Channel serverChannel;

    @Value("${proxy.verifyKey}")
    public String verifyKey;

    /**
     * 四种角色：
     * 服务端代理、服务端真实
     * 客户端代理、客户端真实
     */
    /**
     * 初始化配置
     */
    public abstract void initConfig();

    /**
     * 处理请求
     */
    public void proxyRequest(Msg msg){
        Msg.Header header = msg.getHeader();
        String visitorId = header.getVisitorId();
        Channel channel = realChannel.get(visitorId);
        if (channel != null) {
            ByteBuf buffer = channel.alloc().buffer(msg.getData().length);
            buffer.writeBytes(msg.getData());
            channel.writeAndFlush(buffer);
        } else {
            ProxyClientTunnel clientTunnel = header.getProxyClientTunnel();
            if (clientTunnel != null) {
                log.info(" 开启真实客户端 {} {} ", clientTunnel.getLocalIp(), clientTunnel.getLocalPort());
            } else {
                log.info(" 未找到隧道id {}", header.getTunnelId());
            }
            new SocketCreateUtil().openClientReal(clientTunnel.getLocalIp(), clientTunnel.getLocalPort(), new TcpConsumer<ByteBuf>(
                    ctx -> handleConnectRealSuccess(ctx, visitorId, msg.getData())
                    , (ctx, buf) -> handleRealMessage(ctx, buf, clientTunnel.getId(), visitorId, header.getSendClientId())
                    , ctx -> handleClientVisitorClose(visitorId, header.getSendClientId()), null).toBootstrap());
        }
    }

    public void handleConnectRealSuccess(ChannelHandlerContext ctx, String vid, byte[] bytes) {
        try {
            ByteBuf buf = ctx.alloc().buffer(bytes.length);
            buf.writeBytes(bytes);
            Channel channel = ctx.channel();
            realChannel.put(vid, channel);
            channel.writeAndFlush(buf);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 读取到真实服务数据（局域网服务 -> 响应到真实服务，用于将响应报文的数据发送到服务端代理）
     */
    public void handleRealMessage(ChannelHandlerContext ctx, ByteBuf buf, Integer tunnelId, String vid, String sendClientId) {
        byte[] bytes = new byte[buf.readableBytes()];
        buf.readBytes(bytes);
        sendMsg(Msg.PROXY_RESPONSE, new Msg.Header(clientId, tunnelId, vid, verifyKey, sendClientId, null), bytes, serverChannel);
    }

    public void sendMsg(String type, Msg.Header header, byte[] bytes, Channel channel){
        Msg.sendMsg(type, header, bytes, channel);
    }

    /**
     * 服务端访客关闭事件（服务端访客服务 -> 客户端访客服务，用于释放资源）
     */
    public void handleServerVisitorClose(String vid){
        SocketCreateUtil.closeChannel(realChannel, vid);
    }

    /**
     * 客户端访客关闭事件（客户端访客服务 -> 服务端访客服务，用于解决 旧http请求bug问题）
     */
    public void handleClientVisitorClose(String vid, String sendClientId){
        sendMsg(Msg.VISITOR_CLIENT_CLOSE, new Msg.Header(clientId, null, vid, verifyKey, sendClientId, null), "关闭".getBytes(), serverChannel);
    }

    /**
     * 关闭所有资源
     */
    public void closeAll(){
        SocketCreateUtil.closeChannelAll(realChannel);
    }
}
